package com.yutianran.springBoot.spider;

import com.alibaba.fastjson.JSON;
import com.yutianran.springBoot.spider.impl.DefaultWriter;
import com.yutianran.springBoot.util.FileUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class SpiderBuilder {
    private static SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd-hh-mm-ss");

    private LinkedList<String> urls = new LinkedList<String>();
    private String destDir = "blog";
    private IParser parser;
    private boolean isReverse = false;
    private boolean isWeb = true;
    private IWriter writer = new DefaultWriter();
    private String writerType = "txt";
    private Map<String, String> cookies = new HashMap<String, String>();
    private String title;
    private int threadCount = 10;
    private List<Node> nodeList = new ArrayList<Node>();
    private CountDownLatch startLatch;

    public SpiderBuilder setUrls(String... urls) {
        for (String url : urls) {
            this.urls.add(url);
        }
        return this;
    }

    public SpiderBuilder addUrl(String url) {
        this.urls.add(url);
        return this;
    }

    public SpiderBuilder setUrls(List<String> urls) {
        this.urls.addAll(urls);
        return this;
    }

    public SpiderBuilder setDestDir(String destDir) {
        this.destDir = destDir;
        return this;
    }

    public SpiderBuilder setParser(IParser parser) {
        this.parser = parser;
        return this;
    }

    public SpiderBuilder isReverse(boolean reverse) {
        isReverse = reverse;
        return this;
    }

    public SpiderBuilder setTitle(String title) {
        this.title = title;
        return this;
    }

    /**
     * 获取待处理的url
     *
     * @return
     */
    public synchronized List<String> getUrls() {
        return urls;
    }

    /**
     * 获取存放处理好的数据的List
     *
     * @return
     */
    public synchronized List<Node> getNodeList() {
        return nodeList;
    }

    /**
     * 导入由插件EditThisCookies生产的json格式的cookie
     *
     * @param cookiesFile
     * @return
     */
    public SpiderBuilder setCookiesFile(String cookiesFile) {
        String path = getClass().getResource(cookiesFile).getPath();
        String string = FileUtil.readBytesToString(new File(path));
        List<CookiesBean> list = JSON.parseArray(string, CookiesBean.class);
        for (CookiesBean cookie : list) {
            cookies.put(cookie.getName(), cookie.getValue());
        }
        return this;
    }

    public SpiderBuilder setCookies(Map<String, String> cookies) {
        this.cookies = cookies;
        return this;
    }

    public SpiderBuilder isWeb(boolean web) {
        isWeb = web;
        return this;
    }

    public SpiderBuilder setWriter(IWriter writer) {
        this.writer = writer;
        return this;
    }

    public SpiderBuilder setWriterType(String writerType) {
        this.writerType = writerType;
        return this;
    }

    public SpiderBuilder setThreadCount(int threadCount) {
        this.threadCount = threadCount;
        return this;
    }

    public void run() throws Exception {
        if (parser == null) {
            throw new RuntimeException("必须设置IParser");
        }
        spide();
    }

    private void spide() throws Exception {
        final CountDownLatch latch = new CountDownLatch(urls.size());
        ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount, 200, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        for (final String url : urls) {
            executor.submit(new Runnable() {
                public void run() {
                    try {
                        System.out.println(String.format("当前线程：%s 正在处理：%s", Thread.currentThread().getName(), url));
                        Document document;
                        if (isWeb) {
                            document = Jsoup.connect(url)
                                    .cookies(cookies)
                                    //模拟Chrome浏览器
                                    .userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11")
                                    .get();

                        } else {
                            String path = getClass().getResource(url).getPath();
                            File file = new File(path);
                            document = Jsoup.parse(file, "UTF-8", "");
                        }
                        if (title == null) {
                            title = document.select("head").select("title").first().text();
                        }
                        addNodes(parser.parse(document));
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        latch.countDown();
                    }
                }
            });
        }

        //等待所有子任务全部完成
        latch.await();
        if (isReverse) {
            Collections.reverse(nodeList);
        }
        save(nodeList, title, destDir);
        executor.shutdown();
    }

    /**
     * 保存NodeList
     *
     * @param nodes
     */
    private synchronized void addNodes(List<Node> nodes) {
        nodeList.addAll(nodes);
    }

    private void save(List<Node> list, String title, String dirName) {
        long timeMillis = System.currentTimeMillis();
        String dataTime = ft.format(timeMillis);
        File dir = new File(getClass().getClassLoader().getResource(dirName).getPath());
        File file = new File(dir.getPath() + "/out", title + "-" + dataTime + "." + writerType);
        FileUtil.requireFileNotNull(file);
        FileUtil.writeString(file, writer.write(list));
        System.out.println("Html文件解析并输出到：" + file.getAbsolutePath());
    }

}
